////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Crytek Character Animation source code
//	
//	History:
//	12/01/2005 - Created by Ivo Herzeg <ivo@crytek.de>
//
//  Contains:
//  start rendering 
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include <CryHeaders.h>
#include <CryAnimationScriptCommands.h>
#include <Cry_Camera.h>
#include "ModelMesh.h"
#include <I3DEngine.h>
#include <IShader.h>
#include <CryArray.h>
#include "CharacterInstance.h"
#include "CharacterManager.h"
#include "CryCharAnimationParams.h"
#include "CryCharMorphParams.h"
#include "IRenderAuxGeom.h"
#include "RenderObjectDefines.h"


//! Render object ( register render elements into renderer )
void CCharInstance::Render(const struct SRendParams& RendParams, const QuatTS& Offset, bool* updated)
{
	if( GetFlags() & CS_FLAG_COMPOUND_BASE )
	{
		if (Console::GetInst().ca_DrawCC == 0)
			return;
	}

	if (m_pModel && m_pModel->m_ObjectType == CGA)
	{
		if (Console::GetInst().ca_DrawCGA == 0)
			return;
	}

	if (m_pModel && m_pModel->m_ObjectType == CHR)
	{
		if (Console::GetInst().ca_DrawCHR == 0)
			return;
	}

	if (!(m_rpFlags & CS_FLAG_DRAW_MODEL))
		return;

	assert(RendParams.pMatrix);

	Vec3 position;
	position = RendParams.pMatrix->GetTranslation();
	if (m_SkeletonAnim.m_AnimationDrivenMotion == 0)
		position += Offset.t;

	Matrix33 orientation;
	orientation = Matrix33(*RendParams.pMatrix);
	if (m_SkeletonPose.m_bPhysicsRelinquished)
		orientation = Matrix33(m_PhysEntityLocation.q);

	Matrix34 RenderMat34(orientation, position);

	//f32 axisX = RenderMat34.GetColumn0().GetLength();
	//f32 axisY = RenderMat34.GetColumn1().GetLength();
	//f32 axisZ = RenderMat34.GetColumn2().GetLength();
	//f32 fScaling = 0.333333333f*(axisX+axisY+axisZ);
	//RenderMat34.OrthonormalizeFast();

#ifdef ALLOW_CAMERA_SPACE
  if (Console::GetInst().ca_FPWeaponInCamSpace && m_fFPWeapon)
	{
		Matrix34 CamMat = g_pISystem->GetViewCamera().GetMatrix();
		Vec3 WPos = RenderMat34.GetTranslation(); //position of the object in world-space
		Vec3 CPos = CamMat.GetTranslation();
		Vec3 Dif	=	WPos-CPos;
		Matrix33 c33 = Matrix33(CamMat);
		SmoothCD(m_vDifSmooth, m_vDifSmoothRate, m_fOriginalDeltaTime, Dif*c33, m_fFPWeapon);

		//seems to be a fp-weapon
	//	float fColor[4] = {1,0,1,1};
	//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"m_fFPWeapon: %f    Model: %s ",m_fFPWeapon,  m_pModel->GetFilePath().c_str() );	
	//	g_YLine+=16.0f;

		////overwrite the old world-matrix
		SRendParams* pRendParams = (SRendParams*)&RendParams;
		//pRendParams->pMatrix = &CamMat;								//we take the rotation of the camera
		pRendParams->pMatrix = &RenderMat34;						//Beni - Use weapon matrix, not camera one ;0
		pRendParams->pMatrix->SetTranslation(c33*m_vDifSmooth);   //put the position to ZERO, because we use this object in camera space
		pRendParams->dwFObjFlags |= FOB_CAMERA_SPACE;
		RenderMat34 = (*RendParams.pMatrix);
	}
#endif

	uint32 nFrameID=g_pCharacterManager->m_nUpdateCounter; //g_pIRenderer->GetFrameID(false);
	if (m_LastRenderedFrameID != nFrameID)
	{
		m_LastRenderedFrameID=nFrameID;
		if (m_rpFlags & CS_FLAG_UPDATE_ONRENDER)
		{
			//Usually we update characters by the entity-system. If this is not the case, then we need an UpdateOnRender.
			//To make sure we don't update the same object twice, we compare the FrameID with the last UpdateID.
			//if it is bigger then 3 then the object received no update by the entity-system and an UpdateOnRender is required. 
			uint32 dif = m_LastRenderedFrameID-m_LastUpdateFrameID_Pre;
			uint32 bNeedUpdateOnRender = (dif>3);
			//	float fColor[4] = {0,1,0,1};
			//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"NeedUpdateOnRender: %d    dif: %d    Model: %s",bNeedUpdateOnRender, dif, GetModelFilePath());	g_YLine+=16.0f;
			if (bNeedUpdateOnRender)
			{
				CCamera* pCamera = &(g_pISystem->GetViewCamera());
				Matrix34 ORenderMat34 = RenderMat34; 
				ORenderMat34.OrthonormalizeFast();
				QuatT renderLocation = QuatT(ORenderMat34);
				QuatTS animLocation = renderLocation;// * Offset;
				animLocation.s = Offset.s;

		//		float fColor[4] = {0,1,0,1};
		//		g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"fBoidScaling: (%f %f %f)    %f",Offset.t.x,Offset.t.y,Offset.t.z,  Offset.s);	g_YLine+=16.0f;
				SkeletonPreProcess(renderLocation, animLocation, *pCamera,1);
				SetPostProcessParameter(renderLocation, animLocation, 0, RendParams.fDistance, 1);
				FinishAnimationComputations();
				if(updated)
					*updated = true;
			}
		}
	}

	g_pAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );


	//-----------------------------------------------------------------------------
	//---              map logical LOD to render LOD                            ---
	//-----------------------------------------------------------------------------
	uint32 nLodLevel = RendParams.nLod;
	if(gEnv->IsEditor() && CSkinInstance::m_bForceLOD)
		nLodLevel = CSkinInstance::m_ForcedLODNum;
	if(!gEnv->p3DEngine->IsRenderingIntoShadowMap())
	  m_nAnimationLOD = nLodLevel;

	int nNewLodLevel = nLodLevel;
	static ICVar* p_e_char_lod_min = gEnv->pConsole->GetCVar("e_CharLodMin");
	static ICVar* p_e_lod_min = gEnv->pConsole->GetCVar("e_LodMin");
	if (p_e_char_lod_min)
		nNewLodLevel = max(nNewLodLevel, p_e_char_lod_min->GetIVal());
	if (p_e_lod_min)
		nNewLodLevel = max(nNewLodLevel, p_e_lod_min->GetIVal());

	int32 numLODs = m_pModel->m_arrModelMeshes.size();
	if(nNewLodLevel<m_pModel->m_nBaseLOD)
		nNewLodLevel=m_pModel->m_nBaseLOD;
	if(nNewLodLevel>=numLODs && numLODs > 0)
		nNewLodLevel= max(0,numLODs-1);

	m_nRenderLOD = clamp_tpl(nNewLodLevel,(int32)0,(int32)g_nMaxGeomLodLevels-1);

//	float fColor[4] = {1,0,1,1};
//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"fDistance: %f m_nAnimationLOD: %d   m_nRenderLOD: %d   numLODs: %d  m_pModel->m_nBaseLOD: %d  Model: %s",RendParams.fDistance,m_nAnimationLOD,m_nRenderLOD,numLODs,m_pModel->m_nBaseLOD,m_pModel->GetFilePath().c_str() );	
//	g_YLine+=16.0f;


	if ( !g_pI3DEngine->IsRenderingIntoShadowMap() )
		m_nMorphTargetLod = m_nRenderLOD;

	//------------------------------------------------------------------------

/*
	if ( Console::GetInst().ca_DrawPositionPost ) 	
	{
		Vec3 wpos = RenderMat34.GetTranslation();
		g_pAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
		static Ang3 angle(0,0,0); 
		angle += Ang3(0.01f,0.02f,0.03f);
		AABB aabb = AABB(Vec3(-0.055f,-0.055f,-0.055f),Vec3(+0.055f,+0.055f,+0.055f));
		OBB obb=OBB::CreateOBBfromAABB( Matrix33::CreateRotationXYZ(angle),aabb );
		g_pAuxGeom->DrawOBB(obb,wpos,0,RGBA8(0x00,0xff,0x00,0xff), eBBD_Extremes_Color_Encoded);

		Vec3 axisX = RenderMat34.GetColumn0();
		Vec3 axisY = RenderMat34.GetColumn1();
		Vec3 axisZ = RenderMat34.GetColumn2();
		g_pAuxGeom->DrawLine(wpos,RGBA8(0x7f,0x00,0x00,0x00), wpos+axisX,RGBA8(0xff,0x00,0x00,0x00) );
		g_pAuxGeom->DrawLine(wpos,RGBA8(0x00,0x7f,0x00,0x00), wpos+axisY,RGBA8(0x00,0xff,0x00,0x00) );
		g_pAuxGeom->DrawLine(wpos,RGBA8(0x00,0x00,0x7f,0x00), wpos+axisZ,RGBA8(0x00,0x00,0xff,0x00) );
	}*/

	if (m_pModel->m_ObjectType==CGA)
  {
    Matrix34 mRendMat34 = RenderMat34*Matrix34(Offset);
    RenderCGA (RendParams, mRendMat34);
  }
	else
	{
		pe_params_flags pf;

		if (m_pSkinAttachment)
		{
			//this is a skin-instance. We need to access the skeleton of the master 
			//	CAttachment* pAttachment	= (CAttachment*)m_pIMasterAttachment;
			//	CCharInstance* pIMasterInstance	=	pAttachment->m_pAttachmentManager->m_pCharInstance;

			//	IPhysicalEntity* pCharPhys = pIMasterInstance->m_SkeletonPose.GetCharacterPhysics();
			//	if (pCharPhys && pCharPhys->GetType()==PE_ARTICULATED && pCharPhys->GetParams(&pf) && pf.flags & aef_recorded_physics)
			//		RenderMat34 = RenderMat34*Matrix34(Offset);

      // at min for decals
			int nList = (int)CharacterManager::GetRendererMainThreadId();
      uint32 numRemapJoints	= m_pSkinAttachment->m_arrRemapTable.size();
//      uint32 iActiveThis = this->m_iActiveFrame;
  //    uint32 iActiveFrameMaster	=	m_pSkinAttachment->m_pAttachmentManager->m_pSkelInstance->m_iActiveFrame;

			QuatTS* pSkinningTransformations = GetSkinningTransformations(nList);
			QuatTS* pAttachmentSkinningTransformations = m_pSkinAttachment->m_pAttachmentManager->m_pSkelInstance->GetSkinningTransformations(nList);

      for (uint32 i=0; i<numRemapJoints; i++)
      {
        int index = m_pSkinAttachment->m_arrRemapTable[i];
        pSkinningTransformations[i] = pAttachmentSkinningTransformations[index];
      }
      //
			RenderCHR (RendParams, RenderMat34 );
		} 
		else 
		{
			IPhysicalEntity* pCharPhys = m_SkeletonPose.GetCharacterPhysics();
			if (pCharPhys && pCharPhys->GetType()==PE_ARTICULATED && pCharPhys->GetParams(&pf) && pf.flags & aef_recorded_physics)
				RenderMat34 = RenderMat34*Matrix34(Offset);

			//	float fColor[4] = {0,1,0,1};
			//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"Offset: (%f %f %f)",Offset.t.x,Offset.t.y,Offset.t.z);	
			//	g_YLine+=16.0f;

			RenderCHR (RendParams, RenderMat34 );
		}
	}

	// draw weapon and binded objects
	m_AttachmentManager.DrawAttachments(RendParams,RenderMat34);

  if ( Console::GetInst().ca_FPWeaponInCamSpace && m_fFPWeapon )
    m_bPrevFrameFPWeapon = true;
  else
    m_bPrevFrameFPWeapon = false;
}



void CCharInstance::RenderCGA(const struct SRendParams& RendParams, const Matrix34& RenderMat34 )
{
  if( Console::GetInst().ca_DrawCGAAsSkin==1 && m_pRenderMeshs[0] )
  {
    SRendParams nodeRP = RendParams;
    if( nodeRP.pMaterial==NULL )
    {
      nodeRP.pMaterial = m_pModel->GetMaterial();
    }

		int nList = (int)CharacterManager::GetRendererMainThreadId();
		QuatTS* pSkinningTransformations = GetSkinningTransformations(nList);
    uint32 numJoints = m_SkeletonPose.GetPoseData().GetJointCount();
    assert(numJoints);
    for (uint32 i=0; i<numJoints; i++)
    {
      //SJoint* joint = &m_SkeletonPose.m_arrJoints[i];
      if (m_SkeletonPose.m_arrCGAJoints.size() &&  m_SkeletonPose.m_arrCGAJoints[i].m_CGAObjectInstance)
      {      
        const QuatT& skinTr = m_SkeletonPose.GetPoseData().m_pJointsAbsolute[i];
        pSkinningTransformations[i] = skinTr;
      }
    }
    RenderCHR(nodeRP,RenderMat34);
    return;
  }
	/*
	const char* mname = GetModelFilePath();
	if ( strcmp(mname,"objects/library/architecture/multiplayer/small_vehicle_factory/small_factory_gates.cga")==0 )
	{
	uint32 sdsd=0;
	float fColor[4] = {0,1,0,1};
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"RenderMat34: %f %f %f %f",RenderMat34.m00,RenderMat34.m01,RenderMat34.m02,RenderMat34.m03 );	
	g_YLine+=10.0f;
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"RenderMat34: %f %f %f %f",RenderMat34.m10,RenderMat34.m11,RenderMat34.m12,RenderMat34.m13 );	
	g_YLine+=10.0f;
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"RenderMat34: %f %f %f %f",RenderMat34.m20,RenderMat34.m21,RenderMat34.m22,RenderMat34.m23 );	
	g_YLine+=10.0f;
	}*/

  if(!GetSkinningTransformationCount())
    return;

	SRendParams nodeRP = RendParams;
	Matrix34 ortho_tm34 = RenderMat34;
	IMaterial* pMaterial = nodeRP.pMaterial;
	uint32 numJoints = m_SkeletonPose.GetPoseData().GetJointCount();
	assert(numJoints);
	if ( Console::GetInst().ca_DrawBaseMesh)
	{
		for (uint32 i=0; i<numJoints; i++)
		{
			//SJoint* joint = &m_SkeletonPose.m_arrJoints[i];
			if (m_SkeletonPose.m_arrCGAJoints.size() &&  m_SkeletonPose.m_arrCGAJoints[i].m_CGAObjectInstance)
			{      
				Matrix34 tm34 = ortho_tm34*Matrix34(m_SkeletonPose.GetPoseData().m_pJointsAbsolute[i]);
				nodeRP.pMatrix = &tm34;

				nodeRP.dwFObjFlags |= FOB_TRANS_MASK;
				nodeRP.ppRNTmpData = &m_SkeletonPose.m_arrCGAJoints[i].m_pRNTmpData;      
				nodeRP.pInstance = nodeRP.ppRNTmpData;
      
				// apply custom joint material, if set
				nodeRP.pMaterial = m_SkeletonPose.m_arrCGAJoints[i].m_pMaterial ? m_SkeletonPose.m_arrCGAJoints[i].m_pMaterial.get() : pMaterial;
      
        if(tm34.IsValid())
				  m_SkeletonPose.m_arrCGAJoints[i].m_CGAObjectInstance->Render(nodeRP);
        else
          AnimWarning("CCharInstance::RenderCGA: object has invalid matrix: %s", m_pModel->GetModelFilePath() );
			}
		}
	}

	if (Console::GetInst().ca_DrawSkeleton)
		m_SkeletonPose.DrawSkeleton( RenderMat34 );

	if ( Console::GetInst().ca_DrawBBox ) 
		m_SkeletonPose.DrawBBox( RenderMat34 );
}



void CCharInstance::RenderCHR(const SRendParams& RendParams, const Matrix34& rRenderMat34 )
{
	CRenderObject* pObj = g_pIRenderer->EF_GetObject_Temp();
	if (!pObj)
		return;

	pObj->m_nLod	=	m_nRenderLOD;

	/*float fColor[4] = {1,0,1,1};
	extern f32 g_YLine;
	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"attachment LOD: %d", pObj->m_nLod  ); g_YLine+=0x10;*/

	pObj->m_fSort	= RendParams.fCustomSortOffset;
	pObj->m_ObjFlags |= FOB_TRANS_MASK;
#ifdef ALLOW_CAMERA_SPACE
	if (RendParams.dwFObjFlags & FOB_CAMERA_SPACE)
	{
		pObj->m_ObjFlags |= FOB_CAMERA_SPACE;
	}
#endif
	//check if it should be drawn close to the player
  // For nearest geometry (weapons/arms) - make sure its rendered really at beginning (before water passes)
	if ((RendParams.dwFObjFlags & FOB_NEAREST) || (m_rpFlags & CS_FLAG_DRAW_NEAR) )
  {
    pObj->m_ObjFlags |= FOB_NEAREST;
    ((SRendParams&)RendParams).nAfterWater = 1;
  }
	else
    pObj->m_ObjFlags &= ~FOB_NEAREST;

	pObj->m_fAlpha = RendParams.fAlpha;
	pObj->m_fDistance =	RendParams.fDistance;

	pObj->m_II.m_AmbColor = RendParams.AmbientColor;


	pObj->m_ObjFlags |= RendParams.dwFObjFlags;
	SRenderObjData *pD = g_pIRenderer->EF_GetObjData(pObj, true);

	pObj->m_II.m_Matrix = rRenderMat34;    
  
	pObj->m_nVisAreaStencilRef = RendParams.nVisAreaStencilRef;	
	pObj->m_nTextureID = RendParams.nTextureID;
  
	bool bCheckMotion = MotionBlurMotionCheck( pObj->m_ObjFlags );
	pD->m_pInstance = RendParams.pInstance;
	if( bCheckMotion)
		pObj->m_ObjFlags |= FOB_HAS_PREVSKINXFORM;

	pD->m_nVisionParams = RendParams.nVisionParams;
	pD->m_nHUDSilhouetteParams = RendParams.nHUDSilhouettesParams;

	pObj->m_nMaterialLayers = RendParams.nMaterialLayersBlend;

	if(RendParams.pTerrainTexInfo && (RendParams.dwFObjFlags & (FOB_BLEND_WITH_TERRAIN_COLOR/* | FOB_AMBIENT_OCCLUSION*/)))
	{
		pObj->m_nTextureID = RendParams.pTerrainTexInfo->nTex0;
		//pObj->m_nTextureID1 = RendParams.pTerrainTexInfo->nTex1;
		pD->m_fTempVars[0] = RendParams.pTerrainTexInfo->fTexOffsetX;
		pD->m_fTempVars[1] = RendParams.pTerrainTexInfo->fTexOffsetY;
		pD->m_fTempVars[2] = RendParams.pTerrainTexInfo->fTexScale;
		pD->m_fTempVars[3] = RendParams.pTerrainTexInfo->fTerrainMinZ;
		pD->m_fTempVars[4] = RendParams.pTerrainTexInfo->fTerrainMaxZ;
	}

	pD->m_nCustomData = RendParams.nCustomData;
	pD->m_nCustomFlags = RendParams.nCustomFlags;
	if(RendParams.nCustomFlags & COB_CLOAK_HIGHLIGHT)
	{
		pD->m_fTempVars[5] = RendParams.fCloakHighlightStrength;
	}
  pObj->m_DissolveRef = RendParams.nDissolveRef;

	pD->m_pCharInstance = this;	
	pObj->m_ObjFlags |= FOB_CHARACTER | FOB_INSHADOW;

	if (g_pI3DEngine->IsTessellationAllowed(pObj))
	{
		// Allow this RO to be tessellated, however actual tessellation will be applied if enabled in material
		pObj->m_ObjFlags |= FOB_ALLOW_TESSELLATION;
	}

	{
		// IZF: Note that this actually needs the list id from the renderer
		// itself, not the local list id.
		// Ideally this logic should be moved outside CryAnimation as it is very
		// renderer specific.
		int nList = (int)g_pIRenderer->EF_Query(EFQ_MainThreadList);
		pObj->m_DynLMMask[nList] = RendParams.nDLightMask;
		pD->m_ShadowCasters = RendParams.m_ShadowMapCasters;
		pObj->m_bHasShadowCasters = RendParams.m_ShadowMapCasters != 0;
	}

	pObj->m_nSort = fastround_positive(RendParams.fDistance * 2.0f);

	if (m_HideMaster==0) 
	{
		//	AddCurrentRenderData (pObj, RendParams);
		IRenderMesh* pRenderMesh = m_pRenderMeshs[m_nRenderLOD];
		if (pRenderMesh==0)
			AnimWarning("model '%s' has no render mesh", m_pModel->GetModelFilePath() );

		if (pRenderMesh)
		{       
			// MichaelS - use the instance's material if there is one, and if no override material has
			// already been specified.
			IMaterial* pMaterial = RendParams.pMaterial;
			if (!pMaterial && this->GetMaterialOverride())
				pMaterial = this->GetMaterialOverride();

      if(!pMaterial)
        pMaterial = m_pModel->GetMaterial();
#ifndef _RELEASE
			CModelMesh* pModelMesh = m_pModel->GetModelMesh(m_nRenderLOD);
			static ICVar *p_e_debug_draw = gEnv->pConsole->GetCVar("e_DebugDraw");
			if(p_e_debug_draw && p_e_debug_draw->GetIVal() > 0)
				pModelMesh->DrawDebugInfo(this, m_nRenderLOD, rRenderMat34, p_e_debug_draw->GetIVal(), pMaterial, pObj, RendParams );
#endif
			//	float fColor[4] = {1,0,1,1};
			//	extern f32 g_YLine;
			//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"m_nRenderLOD: %d  %d",RendParams.nLodLevel, pObj->m_nLod  ); g_YLine+=0x10;
			if ( Console::GetInst().ca_DrawBaseMesh)
			{








				pRenderMesh->Render(RendParams,pObj,pMaterial);
				if (!g_pI3DEngine->IsRenderingIntoShadowMap())
					AddDecalsToRenderer(this,0,rRenderMat34);
			}

			g_pAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
			if (Console::GetInst().ca_DrawDecalsBBoxes)
				DrawDecalsBBoxes(this,0, rRenderMat34 );
			if (Console::GetInst().ca_DrawSkeleton)
				m_SkeletonPose.DrawSkeleton( rRenderMat34 );
			if ( Console::GetInst().ca_DrawBBox ) 
				m_SkeletonPose.DrawBBox( rRenderMat34 );
		}
	}

}

//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------

bool CCharInstance::MotionBlurMotionCheck(uint32 nObjFlags)
{
	if (Console::GetInst().ca_MotionBlurMovementThreshold == 0.0f)
		return true;

	if (m_pSkinAttachment)
	{
		CCharInstance* pMaster = m_pSkinAttachment->m_pAttachmentManager->m_pSkelInstance;
		if (pMaster)
			return pMaster->MotionBlurMotionCheck(0);

		return false;
	}

	if  (m_skinningTransformationsMovement > Console::GetInst().ca_MotionBlurMovementThreshold)
		return true;

	return false;
}

//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------

void CSkinInstance::AddCurrentRenderData(CRenderObject *pObj, const SRendParams &pParams)
{
	int nSort = pParams.nRenderList;

	// prevent from render list changing
	// todo: use define here
	int nLod = m_nRenderLOD;
	pObj->m_nLod = nLod;

	IRenderMesh* pRenderMesh = m_pRenderMeshs[nLod];

//	pRenderMesh->SetBBox(m_AABB.min,m_AABB.max);
	pRenderMesh->SetBBox(Vec3(-0.3f,-0.3f,-0.3f),Vec3(+0.3f,+0.3f,+0.3f) );
	IMaterial *pMaterial = pParams.pMaterial;
	if (!pMaterial)
  {
		pMaterial = m_pModel->GetMaterial();
  }
    
  //////////////////////////////////////////////////////////////////////////////////////////////////

	assert(pMaterial && "Material must always be valid here");

	CModelMesh* pSkinning = &m_pModel->m_arrModelMeshes[nLod];
    
  pObj->m_pCurrMaterial = pMaterial;
  pObj->m_nMaterialLayers = pParams.nMaterialLayersBlend;

	if(pParams.pTerrainTexInfo && (pParams.dwFObjFlags & (FOB_BLEND_WITH_TERRAIN_COLOR/* | FOB_AMBIENT_OCCLUSION*/)))
	{
		pObj->m_nTextureID = pParams.pTerrainTexInfo->nTex0;
		//pObj->m_nTextureID1 = pParams.pTerrainTexInfo->nTex1;
    SRenderObjData *pD = g_pIRenderer->EF_GetObjData(pObj, true);
		pD->m_fTempVars[0] = pParams.pTerrainTexInfo->fTexOffsetX;
		pD->m_fTempVars[1] = pParams.pTerrainTexInfo->fTexOffsetY;
		pD->m_fTempVars[2] = pParams.pTerrainTexInfo->fTexScale;
		pD->m_fTempVars[3] = pParams.pTerrainTexInfo->fTerrainMinZ;
		pD->m_fTempVars[4] = pParams.pTerrainTexInfo->fTerrainMaxZ;
	}

  PodArray<CRenderChunk> &Chnks = pRenderMesh->GetChunks();

	for(uint32 i=0; i<Chnks.size(); i++)    
	{ 
		CRenderChunk *pChunk = &pRenderMesh->GetChunks()[i];
		SShaderItem si;

		// Override object material.
		si = pMaterial->GetShaderItem(pChunk->m_nMatID);

		CREMesh * pREOcLeaf  = pChunk->pRE;
	  if (si.m_pShader && pREOcLeaf)
		{
      //// Get material layer manager presets from material resources
      //IMaterial *pCurrMtl = ( pMaterial->GetSubMtlCount() )? pMaterial->GetSubMtl( pChunk->m_nMatID ) : pMaterial;
      //if(pCurrMtl)
      //{
      //  si.m_pShaderResources->m_pMaterialLayerManager = pCurrMtl->GetMaterialLayerManager();
      //} 

			g_pIRenderer->EF_AddEf(pREOcLeaf, si, pObj, nSort);
		}
	}

  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

void CSkinInstance::AddDecalsToRenderer( CCharInstance* pMaster, CAttachment* pAttachment, const Matrix34& RenderMat34 )
{
	if (m_nRenderLOD == 0)
	{
		// TODO: optimize?
		m_DecalManager.RemoveObsoleteDecals();
		size_t numDecals = m_DecalManager.m_arrDecals.size();
		if (!numDecals)
			return;

//		uint32 iActiveFrame		=	pMaster->m_iActiveFrame;
		//IMaterialManager* pMatMan = g_pI3DEngine->GetMaterialManager();
		//IMaterial* pDefaultDecalMaterial( pMatMan->LoadMaterial( "Materials/Decals/CharacterDecal" ) );
		IMaterial* pMaterial;// = pDefaultDecalMaterial;
		//if( pDefaultDecalMaterial )
		//	pDefaultDecalMaterial = g_pI3DEngine->GetMaterialManager()->GetDefaultMaterial();
		//assert( pDefaultDecalMaterial );

		for( size_t d = 0 ; d < numDecals; d++ )
		{
			const CAnimDecal& rDecal = m_DecalManager.m_arrDecals[d];			
			if (rDecal.m_pMaterial)
				pMaterial = rDecal.m_pMaterial;			

			size_t numVertices =rDecal.m_arrVertices.size();
			size_t numFaces	= rDecal.m_arrFaces.size();

		//	float fColor[4] = {0,1,0,1};
		//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"DecalNum: %x  DecalVertices: %x",d,numVertices );	g_YLine+=16.0f;

			if( numFaces > 0 && numVertices > 0 )
			{
				// add decals to renderer
				CRenderObject* pObj( g_pIRenderer->EF_GetObject_Temp() );
        if (!pObj)
          return;
				pObj->m_ObjFlags |= FOB_TRANS_MASK | FOB_DECAL | FOB_NO_Z_PASS | FOB_INSHADOW;
				pObj->m_II.m_Matrix = RenderMat34; // = rRenderMat34;

				SVF_P3F_C4B_T2F* pDecalVertices( 0 );
				SPipTangents* pDecalTangents( 0 );
				uint16* pDecalIndices( 0 );
				g_pIRenderer->EF_AddPolygonToScene( pMaterial->GetShaderItem(), pObj, numVertices, numFaces * 3, pDecalVertices, pDecalTangents, pDecalIndices, true, false );

        int nList = (int)CharacterManager::GetRendererMainThreadId();

				QuatTS* pMasterSkinningTransformations = pMaster->GetSkinningTransformations(nList);
				// no remapping needed for skin attachments as the decal vertices are already generated with the correct skinning transformations
				FillUpDecalBuffer(pMaster, pMasterSkinningTransformations, pDecalVertices, pDecalTangents, pDecalIndices, d);

				//draw rDecal surrounding-lines
				if (Console::GetInst().ca_UseDecals==2) 
				{
					if (numVertices) 
					{
						static std::vector<Vec3> arrVertices;
						arrVertices.resize(numVertices);
						for (uint32 i=0; i<numVertices; i++)
							arrVertices[i] = RenderMat34*pDecalVertices[i].xyz;
						static std::vector<TFace> arrFaces;
						arrFaces.resize(numFaces);
						for (uint32 i=0; i<numFaces; i++)
						{
							arrFaces[i].i0 = pDecalIndices[i*3+0];
							arrFaces[i].i1 = pDecalIndices[i*3+1];
							arrFaces[i].i2 = pDecalIndices[i*3+2];
						}
						SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
						renderFlags.SetFillMode( e_FillModeWireframe );
						renderFlags.SetDrawInFrontMode( e_DrawInFrontOn );
						g_pAuxGeom->SetRenderFlags( renderFlags );
						g_pAuxGeom->DrawTriangles( &arrVertices[0],numVertices,  &arrFaces[0].i0,numFaces*3, RGBA8(0x00,0xff,0x00,0x00) );		
					}
				}
			}
		}
	}
}



//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
void CSkinInstance::FillUpDecalBuffer( CCharInstance* pMaster, QuatTS* parrNewSkinQuat, SVF_P3F_C4B_T2F* pVertices, SPipTangents* pTangents, uint16* pIndices, size_t decalIdx )
{

	CModelMesh* pMesh = m_pModel->GetModelMesh(0);
	
	if( decalIdx == 0 )
		pMesh->InitSkinningExtSW(&m_Morphing,pMaster->m_arrShapeDeformValues,0); // only init for first decal of this instance, all other can reuse prepared data

	const CAnimDecal& rDecal = m_DecalManager.m_arrDecals[decalIdx];

	size_t numDecalVertices = rDecal.m_arrVertices.size();
	size_t numDecalFaces = rDecal.m_arrFaces.size();


	//float fColor[4] = {1,1,0,1};
	//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"numDecalVertices: %d  numDecalFaces: %d",numDecalVertices,numDecalFaces );	g_YLine+=16.0f;


	// fill vertices
	pMesh->LockFullRenderMesh(0);
	for (size_t i=0; i<numDecalVertices; i++)
	{
		// this is the index of the untransformed vertex

		//FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!
		//FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!FIXME!
		uint32 idx = rDecal.m_arrVertices[i].nVertex;
		assert(idx<0x4000);
		pVertices[i].xyz		=	pMesh->GetSkinnedExtVertex2(parrNewSkinQuat,idx);
		pVertices[i].st	=	Vec2(rDecal.m_arrVertices[i].u, rDecal.m_arrVertices[i].v);
		pVertices[i].color.dcolor	=	~0;

		// TODO: fill proper tangents
		pTangents[i].Tangent	=	Vec4sf( tPackF2B( 1.0f ), tPackF2B( 0.0f ), tPackF2B( 0.0f ), tPackF2B( 1 ) );
		pTangents[i].Binormal = Vec4sf( tPackF2B( 0.0f ), tPackF2B( 1.0f ), tPackF2B( 0.0f ), tPackF2B( 1 ) );
	}

	pMesh->UnlockFullRenderMesh(0);
	// fill indices
	for (size_t f=0, g=0; f<numDecalFaces; f++)
	{
		const TFace& face = rDecal.m_arrFaces[f];
		pIndices[g] = face.i0; 
		g++;
		pIndices[g] = face.i1; 
		g++;
		pIndices[g] = face.i2; 
		g++;
	}
}

//////////////////////////////////////////////////////////////////////////
void CSkinInstance::SetMaterial( IMaterial *pMaterial )
{
	m_pInstanceMaterial = pMaterial;
}

//////////////////////////////////////////////////////////////////////////
IMaterial* CSkinInstance::GetMaterial()
{
	if (m_pInstanceMaterial)
		return m_pInstanceMaterial;
	return m_pModel->GetMaterial();
}

//////////////////////////////////////////////////////////////////////////
IMaterial* CSkinInstance::GetMaterialOverride()
{
	return m_pInstanceMaterial;
}

IRenderMesh* CSkinInstance::GetRenderMesh ()
{
	assert( m_nRenderLOD >= 0 && m_nRenderLOD < g_nMaxGeomLodLevels);
	return m_pRenderMeshs[m_nRenderLOD];
}

// this is the array that's returned from the RenderMesh
const PodArray<CRenderChunk>* CSkinInstance::getRenderMeshMaterials()
{
	if (m_pModel->m_ObjectType==CGA)
	{
		return 0;
	}
	else
	{
		if (m_pRenderMeshs[m_pModel->m_nBaseLOD])
			return &m_pRenderMeshs[m_pModel->m_nBaseLOD]->GetChunks();
		else
			return NULL;
	}
}






































































//-----------------------------------------------------------------------
//----        add all morph-targets into a dynamic-stream            ----
//-----------------------------------------------------------------------
void CSkinInstance::AddMorphTargetsToVertexBuffer_Verified( int nLOD,uint32 nCloseEnough, uint32 &HWSkinningFlags )
{
	// [MichaelS - 4/8/2007] There is a problem with this method of updating the VB only once per
	// frame. The problem is that the character is often rendered twice - the first time for shadow
	// casting. Sometimes the shadow casting uses a lower lod than the main rendering, so the morph
	// targets are calculated for the wrong lod, leading to incorrect morph playback when the mesh
	// is rendered. Therefore, when Render() is called, we check whether the rendering is for shadow
	// casting, and if not we set the m_nPendingMorphTargetLod to the appropriate lod. This might
	// mean that the shadow casting is done using the morphs from the previous frame. On the other
	// hand it might mean that the shadow casting is done using morphs from an incorrect lod, but
	// this causes fewer problems than rendering visible geometry with the wrong lod.

	//Note: the check for the correctness of the LOD is not in an inlined function in the header - Rich S






























	
	CryAutoLock<CryCriticalSectionNonRecursive> lock(m_Morphing.m_lock);
	
	CModelMesh* pModelMesh = m_pModel->GetModelMesh(nLOD);
	
	if (m_Morphing.m_LinearMorphSequence<0)
	{
		//	uint32 needMorph = (nLOD==0 && (NeedMorph() || m_nStillNeedsMorph > 0) && Console::GetInst().ca_UseMorph);
		uint32 needMorph = ( nCloseEnough && (m_Morphing.NeedMorph() || m_nStillNeedsMorph > 0) && Console::GetInst().ca_UseMorph);
		HWSkinningFlags |=  needMorph ? eHWS_MorphTarget : 0;

		if (HWSkinningFlags & eHWS_MorphTarget)
		{
			DEFINE_PROFILER_SECTION("CSkinInstance::MorphTargetCopy");
			//memset(pMorphData,0,numExtVertices*sizeof(Vec3));
			uint32 numVertices = pModelMesh->m_numExtVertices;
			assert(numVertices);





			PIXBeginNamedEvent(0, "[Morph Target] running on MT");

			if (!m_UpdatedMorphTargetVBThisFrame)
			{
				if (m_Morphing.NeedMorph())
				{
					m_nStillNeedsMorph = 30; // Continue morph targets for at least 100 times after all morph targets gone.
				}

				m_Morphing.UpdateMorph(s_morphs, nLOD, numVertices);
				m_UpdatedMorphTargetVBThisFrame = true;
			}
			m_Morphing.UpdateVBStream(numVertices, m_pRenderMeshs[nLOD]);

			PIXEndNamedEvent();
		}
	}
	else
	{
		//linear playback of morphs
		DEFINE_PROFILER_SECTION("CSkinInstance::MorphTargetCopy");
    int32 nStride;
		m_pRenderMeshs[nLOD]->LockForThreadAccess();
    Vec3* pMorphData = (Vec3 *)m_pRenderMeshs[ nLOD ]->GetMorphTargetPtr(nStride, FSL_SYSTEM_CREATE);
    assert(pMorphData);
		uint32 numVertices = pModelMesh->m_numExtVertices;
		assert(numVertices);

		if (!m_UpdatedMorphTargetVBThisFrame)
		{
			m_UpdatedMorphTargetVBThisFrame = true;
			if (m_Morphing.m_morphTargetsState.size() != numVertices)
				m_Morphing.m_morphTargetsState.resize(numVertices);
			memset( &m_Morphing.m_morphTargetsState[0],0,numVertices*sizeof(Vec3) );

			uint32 numMorphs=pModelMesh->m_morphTargets.size();
			if (numMorphs>1)
			{
				f32 t=m_Morphing.m_LinearMorphSequence;
				if (t>=1)	t=0.9999f;

				Vec3 UnpackedVertex;

				f32 blend  = t*(numMorphs-1);
				uint32 Morph0 = uint32(blend);
				uint32 Morph1 = Morph0+1;

				assert(Morph0!=Morph1);
				CMorphTarget* pMorphTarget0 = pModelMesh->m_morphTargets[Morph0];
				f32 t0=1.0f-(blend-Morph0);
				uint32 numMorphVertices0 = pMorphTarget0->m_vertices.size();
				for (uint32 i=0; i<numMorphVertices0; i++)
				{
					f32 x=f32(pMorphTarget0->m_vertices[i].m_MTVertexX)/255.0f;
					f32 y=f32(pMorphTarget0->m_vertices[i].m_MTVertexY)/255.0f;
					f32 z=f32(pMorphTarget0->m_vertices[i].m_MTVertexZ)/255.0f;
					UnpackedVertex.x = x*pMorphTarget0->m_MTExtensions.x+pMorphTarget0->m_MTNegBasis.x;
					UnpackedVertex.y = y*pMorphTarget0->m_MTExtensions.y+pMorphTarget0->m_MTNegBasis.y;
					UnpackedVertex.z = z*pMorphTarget0->m_MTExtensions.z+pMorphTarget0->m_MTNegBasis.z;
					m_Morphing.m_morphTargetsState[pMorphTarget0->m_vertices[i].m_nVertexId].xyz = UnpackedVertex*t0;
				}
				f32 t1=(blend-Morph0);
				CMorphTarget* pMorphTarget1 = pModelMesh->m_morphTargets[Morph1];
				uint32 numMorphVertices1 = pMorphTarget1->m_vertices.size();
				for (uint32 i=0; i<numMorphVertices1; i++)
				{
					f32 x=f32(pMorphTarget1->m_vertices[i].m_MTVertexX)/255.0f;
					f32 y=f32(pMorphTarget1->m_vertices[i].m_MTVertexY)/255.0f;
					f32 z=f32(pMorphTarget1->m_vertices[i].m_MTVertexZ)/255.0f;
					UnpackedVertex.x = x*pMorphTarget1->m_MTExtensions.x+pMorphTarget1->m_MTNegBasis.x;
					UnpackedVertex.y = y*pMorphTarget1->m_MTExtensions.y+pMorphTarget1->m_MTNegBasis.y;
					UnpackedVertex.z = z*pMorphTarget1->m_MTExtensions.z+pMorphTarget1->m_MTNegBasis.z;
					m_Morphing.m_morphTargetsState[pMorphTarget1->m_vertices[i].m_nVertexId].xyz = m_Morphing.m_morphTargetsState[pMorphTarget1->m_vertices[i].m_nVertexId].xyz + UnpackedVertex*t1;
				}
				//float fColor[4] = {0,1,0,1};
				//const char* morphname = pMorphTarget0->m_name;
				//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"t0: %f    t1: %f ",t0,t1 );	g_YLine+=16.0f;
				//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"numMorphs: %d %d %f %d  morphname: %s  numVertices:%d numMorphVertices:%d",numMorphs, Morph0,blend,Morph1,morphname,numVertices,numMorphVertices0 );	g_YLine+=16.0f;
			}
		}	//if(m_RenderPass==0)

		// Copy morph targets state into the per pass data.
		HWSkinningFlags |=  eHWS_MorphTarget;
		cryMemcpy( pMorphData,&m_Morphing.m_morphTargetsState[0],numVertices*sizeof(Vec3) );
		m_pRenderMeshs[nLOD]->UnlockStream(VSF_HWSKIN_MORPHTARGET_INFO);
		m_pRenderMeshs[nLOD]->UnLockForThreadAccess();
	}
}








